/*
 * FreeRTOS Kernel V10.3.1
 * Copyright (C) 2020 Amazon.com, Inc. or its affiliates.  All Rights Reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 * the Software, and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 * http://www.FreeRTOS.org
 * http://aws.amazon.com/freertos
 *
 * 1 tab == 4 spaces!
 */

/*
 * Changes from V3.0.0
 *
 * Changes from V3.0.1
 */
#ifndef PORTMACRO_H
    #define PORTMACRO_H

    #if !defined( _SERIES ) || _SERIES != 18
        #error "WizC supports FreeRTOS on the Microchip PIC18-series only"
    #endif

    #if !defined( QUICKCALL ) || QUICKCALL != 1
        #error "QuickCall must be enabled (see ProjectOptions/Optimisations)"
    #endif

    #include <stddef.h>
    #include <pic.h>

    #define portCHAR          char
    #define portFLOAT         float
    #define portDOUBLE        portFLOAT
    #define portLONG          long
    #define portSHORT         short
    #define portSTACK_TYPE    uint8_t
    #define portBASE_TYPE     char

    typedef portSTACK_TYPE   StackType_t;
    typedef signed char      BaseType_t;
    typedef unsigned char    UBaseType_t;


    #if ( configUSE_16_BIT_TICKS == 1 )
        typedef uint16_t     TickType_t;
        #define portMAX_DELAY     ( TickType_t ) ( 0xFFFF )
    #else
        typedef uint32_t     TickType_t;
        #define portMAX_DELAY     ( TickType_t ) ( 0xFFFFFFFF )
    #endif

    #define portBYTE_ALIGNMENT    1

/*-----------------------------------------------------------*/

/*
 * Constant used for context switch macro when we require the interrupt
 * enable state to be forced when the interrupted task is switched back in.
 */
    #define portINTERRUPTS_FORCED           ( 0x01 )

/*
 * Constant used for context switch macro when we require the interrupt
 * enable state to be unchanged when the interrupted task is switched back in.
 */
    #define portINTERRUPTS_UNCHANGED        ( 0x00 )

/* Initial interrupt enable state for newly created tasks.  This value is
 * used when a task switches in for the first time.
 */
    #define portINTERRUPTS_INITIAL_STATE    ( portINTERRUPTS_FORCED )

/*
 * Macros to modify the global interrupt enable bit in INTCON.
 */
    #define portDISABLE_INTERRUPTS() \
    do                               \
    {                                \
        bGIE = 0;                    \
    } while( bGIE ) /* MicroChip recommends this check! */

    #define portENABLE_INTERRUPTS() \
    do                              \
    {                               \
        bGIE = 1;                   \
    } while( 0 )

/*-----------------------------------------------------------*/

/*
 * Critical section macros.
 */
    extern uint8_t ucCriticalNesting;

    #define portNO_CRITICAL_SECTION_NESTING    ( ( uint8_t ) 0 )

    #define portENTER_CRITICAL()  \
    do                            \
    {                             \
        portDISABLE_INTERRUPTS(); \
                                  \
        /* \
         * Now interrupts are disabled ucCriticalNesting \
         * can be accessed directly. Increment \
         * ucCriticalNesting to keep a count of how \
         * many times portENTER_CRITICAL() has been called. \
         */                  \
        ucCriticalNesting++; \
    } while( 0 )

    #define portEXIT_CRITICAL()                                   \
    do                                                            \
    {                                                             \
        if( ucCriticalNesting > portNO_CRITICAL_SECTION_NESTING ) \
        {                                                         \
            /* \
             * Decrement the nesting count as we are leaving a \
             * critical section. \
             */                  \
            ucCriticalNesting--; \
        }                        \
                                 \
        /* \
         * If the nesting level has reached zero then \
         * interrupts should be re-enabled. \
         */                                                        \
        if( ucCriticalNesting == portNO_CRITICAL_SECTION_NESTING ) \
        {                                                          \
            portENABLE_INTERRUPTS();                               \
        }                                                          \
    } while( 0 )

/*-----------------------------------------------------------*/

/*
 * The minimal stacksize is calculated on the first reference of
 * portMINIMAL_STACK_SIZE. Some input to this calculation is
 * compiletime determined, other input is port-defined (see port.c)
 */
    extern uint16_t usPortCALCULATE_MINIMAL_STACK_SIZE( void );
    extern uint16_t usCalcMinStackSize;

    #define portMINIMAL_STACK_SIZE           \
    ( ( usCalcMinStackSize == 0 )            \
      ? usPortCALCULATE_MINIMAL_STACK_SIZE() \
      : usCalcMinStackSize )

/*
 * WizC uses a downgrowing stack
 */
    #define portSTACK_GROWTH    ( -1 )

/*-----------------------------------------------------------*/

/*
 * Macro's that pushes all the registers that make up the context of a task onto
 * the stack, then saves the new top of stack into the TCB. TOSU and TBLPTRU
 * are only saved/restored on devices with more than 64kB (32k Words) ROM.
 *
 * The stackpointer is helt by WizC in FSR2 and points to the first free byte.
 * WizC uses a "downgrowing" stack. There is no framepointer.
 *
 * We keep track of the interruptstatus using ucCriticalNesting. When this
 * value equals zero, interrupts have to be enabled upon exit from the
 * portRESTORE_CONTEXT macro.
 *
 * If this is called from an ISR then the interrupt enable bits must have been
 * set for the ISR to ever get called.  Therefore we want to save
 * ucCriticalNesting with value zero. This means the interrupts will again be
 * re-enabled when the interrupted task is switched back in.
 *
 * If this is called from a manual context switch (i.e. from a call to yield),
 * then we want to keep the current value of ucCritialNesting so it is restored
 * with its current value. This allows a yield from within a critical section.
 *
 * The compiler uses some locations at the bottom of RAM for temporary
 * storage. The compiler may also have been instructed to optimize
 * function-parameters and local variables to global storage. The compiler
 * uses an area called LocOpt for this wizC feature.
 * The total overheadstorage has to be saved in it's entirety as part of
 * a task context. These macro's store/restore from data address 0x0000 to
 * (OVERHEADPAGE0-LOCOPTSIZE+MAXLOCOPTSIZE - 1).
 * OVERHEADPAGE0, LOCOPTSIZE and MAXLOCOPTSIZE are compiler-generated
 * assembler definitions.
 */

    #define portSAVE_CONTEXT( ucInterruptForced )                 \
    do                                                            \
    {                                                             \
        portDISABLE_INTERRUPTS();                                 \
                                                                  \
        _Pragma( "asm" )                                          \
        ;                                                         \
        ; Push the relevant SFR 's onto the task's stack          \
        ;                                                         \
        movff STATUS, POSTDEC2                                    \
        movff WREG, POSTDEC2                                      \
        movff BSR, POSTDEC2                                       \
        movff PRODH, POSTDEC2                                     \
        movff PRODL, POSTDEC2                                     \
        movff FSR0H, POSTDEC2                                     \
        movff FSR0L, POSTDEC2                                     \
        movff FSR1H, POSTDEC2                                     \
        movff FSR1L, POSTDEC2                                     \
        movff TABLAT, POSTDEC2                                    \
              if __ROMSIZE > 0x8000                               \
        movff TBLPTRU, POSTDEC2                                   \
        endif                                                     \
        movff TBLPTRH, POSTDEC2                                   \
        movff TBLPTRL, POSTDEC2                                   \
              if __ROMSIZE > 0x8000                               \
        movff PCLATU, POSTDEC2                                    \
        endif                                                     \
        movff PCLATH, POSTDEC2                                    \
        ;                                                         \
        ; Store the compiler - scratch - area as described above. \
           ;                                                      \
        movlw OVERHEADPAGE0 - LOCOPTSIZE + MAXLOCOPTSIZE          \
        clrf FSR0L, ACCESS                                        \
        clrf FSR0H, ACCESS                                        \
_rtos_S1:                                                         \
        movff POSTINC0, POSTDEC2                                  \
        decfsz WREG, W, ACCESS                                    \
        SMARTJUMP _rtos_S1                                        \
        ;                                                         \
        ; Save the pic call / return -stack belonging to the      \
        ; current task by copying it to the task 's software-	\
			; stack. We save the hardware stack pointer (which		\
			; is the number of addresses on the stack) in the		\
			; W-register first because we need it later and it		\
			; is modified in the save-loop by executing pop's.    \
           ; After the loop the W - register is stored on the     \
        ; stack, too.                                             \
           ;                                                      \
        movf STKPTR, W, ACCESS                                    \
        bz _rtos_s3                                               \
_rtos_S2:                                                         \
        if __ROMSIZE > 0x8000                                     \
        movff TOSU, POSTDEC2                                      \
        endif                                                     \
        movff TOSH, POSTDEC2                                      \
        movff TOSL, POSTDEC2                                      \
        pop                                                       \
        tstfsz STKPTR, ACCESS                                     \
        SMARTJUMP _rtos_S2                                        \
_rtos_s3:                                                         \
        movwf POSTDEC2, ACCESS                                    \
        ;                                                         \
        ; Next the value for ucCriticalNesting used by the        \
        ; task is stored on the stack.When                        \
        ; ( ucInterruptForced == portINTERRUPTS_FORCED ), we save \
        ; it as 0 ( portNO_CRITICAL_SECTION_NESTING ).            \
           ;                                                      \
        if ucInterruptForced == portINTERRUPTS_FORCED             \
        clrf POSTDEC2, ACCESS                                     \
        else{                                                     \
            movff ucCriticalNesting, POSTDEC2                     \
           endif                                                  \
            ; }                                                   \
        ; Save the new top of the software stack in the TCB.      \
           ;                                                      \
        movff pxCurrentTCB, FSR0L                                 \
        movff pxCurrentTCB + 1, FSR0H                             \
        movff FSR2L, POSTINC0                                     \
        movff FSR2H, POSTINC0                                     \
        _Pragma( "asmend" )                                       \
    } while( 0 )

/************************************************************/

/*
 * This is the reverse of portSAVE_CONTEXT.
 */
    #define portRESTORE_CONTEXT()                                   \
    do                                                              \
    {                                                               \
        _Pragma( "asm" )                                            \
        ;                                                           \
        ; Set FSR0 to point to pxCurrentTCB->pxTopOfStack.          \
           ;                                                        \
        movff pxCurrentTCB, FSR0L                                   \
        movff pxCurrentTCB + 1, FSR0H                               \
        ;                                                           \
        ; De - reference FSR0 to set the address it holds into      \
        ; FSR2( i.e. *( pxCurrentTCB->pxTopOfStack ) ).FSR2         \
        ; is used by wizC as stackpointer.                          \
           ;                                                        \
        movff POSTINC0, FSR2L                                       \
        movff POSTINC0, FSR2H                                       \
        ;                                                           \
        ; Next, the value for ucCriticalNesting used by the         \
        ; task is retrieved from the stack.                         \
           ;                                                        \
        movff PREINC2, ucCriticalNesting                            \
        ;                                                           \
        ; Rebuild the pic call / return -stack.The number of        \
        ; return addresses is the next item on the task stack.      \
           ; Save this number in PRODL.Then fetch the addresses     \
        ; and store them on the hardwarestack.                      \
           ; The datasheets say we can 't use movff here...			\
			;														\
			movff	PREINC2,PRODL	// Use PRODL as tempregister	\
			clrf	STKPTR,ACCESS									\
		_rtos_R1:													\
			push													\
			movf	PREINC2,W,ACCESS								\
			movwf	TOSL,ACCESS										\
			movf	PREINC2,W,ACCESS								\
			movwf	TOSH,ACCESS										\
			if __ROMSIZE > 0x8000									\
				movf	PREINC2,W,ACCESS							\
				movwf	TOSU,ACCESS									\
			else													\
				clrf	TOSU,ACCESS									\
			endif													\
			decfsz	PRODL,F,ACCESS									\
			SMARTJUMP _rtos_R1										\
			;														\
			; Restore the compiler's working storage area to page 0 \
        ;                                                           \
        movlw OVERHEADPAGE0 - LOCOPTSIZE + MAXLOCOPTSIZE            \
        movwf FSR0L, ACCESS                                         \
        clrf FSR0H, ACCESS                                          \
_rtos_R2:                                                           \
        decf FSR0L, F, ACCESS                                       \
        movff PREINC2, INDF0                                        \
        tstfsz FSR0L, ACCESS                                        \
        SMARTJUMP _rtos_R2                                          \
        ;                                                           \
        ; Restore the sfr 's forming the tasks context.			\
			; We cannot yet restore bsr, w and status because		\
			; we need these	registers for a final test.				\
			;														\
			movff	PREINC2,PCLATH									\
			if __ROMSIZE > 0x8000									\
				movff	PREINC2,PCLATU								\
			else													\
				clrf	PCLATU,ACCESS								\
			endif													\
			movff	PREINC2,TBLPTRL									\
			movff	PREINC2,TBLPTRH									\
			if __ROMSIZE > 0x8000									\
				movff	PREINC2,TBLPTRU								\
			else													\
				clrf	TBLPTRU,ACCESS								\
			endif													\
			movff	PREINC2,TABLAT									\
			movff	PREINC2,FSR1L									\
			movff	PREINC2,FSR1H									\
			movff	PREINC2,FSR0L									\
			movff	PREINC2,FSR0H									\
			movff	PREINC2,PRODL									\
			movff	PREINC2,PRODH									\
			;														\
			; The return from portRESTORE_CONTEXT() depends on		\
			; the value of ucCriticalNesting. When it is zero,		\
			; interrupts need to be enabled. This is done via a		\
			; retfie instruction because we need the				\
			; interrupt-enabling and the return to the restored		\
			; task to be uninterruptable.							\
	        ; Because bsr, status and W are affected by the test	\
	        ; they are restored after the test.						\
			;														\
			movlb	ucCriticalNesting>>8							\
			tstfsz	ucCriticalNesting,BANKED						\
			SMARTJUMP _rtos_R4										\
		_rtos_R3:													\
			movff	PREINC2,BSR										\
			movff	PREINC2,WREG									\
			movff	PREINC2,STATUS									\
			retfie	0		; Return enabling interrupts			\
		_rtos_R4:													\
			movff	PREINC2,BSR										\
			movff	PREINC2,WREG									\
			movff	PREINC2,STATUS									\
			return	0		; Return without affecting interrupts	\
		_Pragma("asmend")											\
	} while(0)

/*-----------------------------------------------------------*/

#define portTICK_PERIOD_MS	( ( TickType_t ) 1000 / configTICK_RATE_HZ )

/*-----------------------------------------------------------*/

extern void vPortYield( void );
#define portYIELD()				vPortYield()

#define portNOP()	_Pragma("asm")									\
						nop											\
					_Pragma("asmend")

/*-----------------------------------------------------------*/

#define portTASK_FUNCTION( xFunction, pvParameters )	    \
	void pointed xFunction( void *pvParameters )		\
	_Pragma(asmfunc xFunction)

#define portTASK_FUNCTION_PROTO		portTASK_FUNCTION
/*-----------------------------------------------------------*/


#define volatile
#define register

#endif /* PORTMACRO_H */












